package a.server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Description:
 * Param:
 * return:
 * Author:20201002983郑雨蓓
 * Date: 2022/9/22
 */
public class ChatServer {
  private int port = 8008; //服务器监听端口
  private ServerSocket serverSocket; //定义服务器套接字
  private ExecutorService executorService = Executors.newCachedThreadPool();
  private static CopyOnWriteArraySet<Socket> members = new CopyOnWriteArraySet<>();//泛型
  private static HashMap<Socket, String> user = new HashMap();

  public ChatServer() throws IOException {
    serverSocket = new ServerSocket(8008);
    System.out.println("服务器启动监听在 " + port + " 端口");
  }

  private PrintWriter getWriter(Socket socket) throws IOException {
    //获得输出流缓冲区的地址
    OutputStream socketOut = socket.getOutputStream();
    //网络流写出需要使用flush，这里在PrintWriter构造方法中直接设置为自动flush
    return new PrintWriter(new OutputStreamWriter(socketOut, "utf-8"), true);
  }

  private BufferedReader getReader(Socket socket) throws IOException {
    //获得输入流缓冲区的地址
    InputStream socketIn = socket.getInputStream();
    return new BufferedReader(
      new InputStreamReader(socketIn, "utf-8"));
  }

  //单客户版本，即每一次只能与一个客户建立通信连接
  public void Service() {
    while (true) {
      Socket socket = null;
      try {
        //此处程序阻塞等待，监听并等待客户发起连接，有连接请求就生成一个套接字。
        socket = serverSocket.accept();
        BufferedReader br = getReader(socket);
        String msg = br.readLine();//获得用户名
        members.add(socket);
        user.put(socket, msg);
        executorService.execute(new Handle(socket));
      } catch (IOException e) {
        e.printStackTrace();
      }

    }
  }


  public class Handle implements Runnable {
    Socket socket;

    Handle(Socket socket) {
      this.socket = socket;
    }

    @Override
    public void run() {
      System.out.println("New connection accepted： " + socket.getInetAddress().getHostAddress());
      try {
        BufferedReader br = getReader(socket);
        PrintWriter pw = getWriter(socket);
        pw.println("From 服务器：登陆成功! 输入users可查询在线用户， 输入bye可退出登录");
        loginToAll(socket);
        online(socket);
        String msg = null;
        //程序阻塞，每次从输入流读一行消息
        while ((msg = br.readLine()) != null) {
          if (msg.trim().equalsIgnoreCase("bye")) {
            //向输出流中输出一行字符串,远程客户端可以读取该字符串
            pw.println("From 服务器：服务器已断开连接，结束服务！");
            System.out.println(socket.getInetAddress() + "客户端离开");
            offline(socket);
            members.remove(socket);
            user.remove(socket);
            break;//跳出循环读取
          }
          if (msg.trim().equalsIgnoreCase("users")) {
            online(socket);
          }
          else if (msg.contains("&&")) {
            int temp = msg.indexOf("&&");//&&第一次出现的索引
            String chatMember = msg.substring(0, temp);
            System.out.println("charMember=" + chatMember);
            String realMsg = msg.substring(temp + 2);
            System.out.println("realMsg=" + realMsg);
            sendToOne(chatMember, realMsg, socket);
          } else {
            String name = user.get(socket);
            sendToAllMember(msg, name);
          }
        }

      } catch (
        IOException e) {
        e.printStackTrace();
      } finally {
        try {
          if (socket != null) {
            socket.close();
//            socket.shutdownOutput();
// 现在 socket 是半关闭状态，输出流关闭，但输入流打开，socket 连接不会断开。
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

  }//Handle_end

  private void sendToAllMember(String msg, String name) throws IOException {
    PrintWriter pw;
    OutputStream out;
    for (Socket tempSocket : members) {//遍历members集合，取出每一个元素
      out = tempSocket.getOutputStream();
      pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"), true);
      pw.println(name + "说： " + msg);
    }
  }

  //私聊
  private void sendToOne(String name, String msg, Socket socket) throws IOException {
    PrintWriter pw;
    OutputStream out;
    OutputStream out2 = socket.getOutputStream();
    Socket chatMember = null;
    boolean flag = false;//如果找得到则true
    for (Socket tempSocket : user.keySet()) {
      if (user.get(tempSocket).equals(name)) {
        chatMember = tempSocket;
        flag = true;
      }
    }
    if (flag) {
      out = chatMember.getOutputStream();
      pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"), true);
      pw.println(user.get(socket) + "私聊你: " + msg);
      pw = new PrintWriter(new OutputStreamWriter(out2, "utf-8"), true);
      pw.println(user.get(socket) + "私聊" + name + ": " + msg);
    } else {
      pw = new PrintWriter(new OutputStreamWriter(out2, "utf-8"), true);
      pw.println(user.get(socket) + "私聊" + name + ": " + msg);
      pw.println("该用户不存在或未上线");
    }
  }

  //登录消息
  private void loginToAll(Socket socket) throws IOException {
    PrintWriter pw;
    OutputStream out;
    for (Socket tempSocket : members) {
      out = tempSocket.getOutputStream();
      pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"), true);
      if (!socket.equals(tempSocket)) {
        pw.println(user.get(socket) + "上线");
      }
    }
  }


  //在线信息广播
  private void online(Socket socket) throws IOException {
    PrintWriter pw;
    OutputStream out;
    out = socket.getOutputStream();
    pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"), true);
    pw.println("在线用户有：");
    for (String msg : user.values())
      pw.println(msg);
  }

  private void offline(Socket socket) throws IOException {
    PrintWriter pw;
    OutputStream out;
    for (Socket tempSocket : members) {
      out = tempSocket.getOutputStream();
      pw = new PrintWriter(new OutputStreamWriter(out, "utf-8"), true);
      if (!socket.equals(tempSocket)) {
        pw.println(user.get(socket) + "下线");
      }
    }
  }


  public static void main(String[] args) throws IOException {
    new ChatServer().Service();
  }

}//end
